#include <gio/gio.h>
#include <glib.h>
#include <stdint.h>
#include <stdbool.h>
#include "socket.h"

#define NET_BUFFER_MAX 1024
typedef int (*func_send)(void *, const uint8_t *, size_t, void *);

static void print_hex(const uint8_t *buffer, size_t size)
{
  gsize i;

  for(i = 0; i < size; i++) {
    g_print("%02x ", (unsigned char)buffer[i]);
  }
  g_print("\n");
}
int tcp_send(void *conn, const uint8_t *buffer, size_t size, void *data)
{
  int ret = false;
  GError *error = NULL;
  GOutputStream *ostream = NULL;

  if (!buffer || !size) {
    g_warning("L%d no buffer or size is zero\n", __LINE__);
    return ret;
  }

  ostream = g_io_stream_get_output_stream(G_IO_STREAM(conn));
  if (g_output_stream_write_all(ostream, buffer, size, NULL, NULL,
        &error)) {
    print_hex(buffer, size);
    ret = true;
  } else {
    g_warning("L%d f-%s error:%s", __LINE__, __func__, error->message);
    g_clear_error(&error);
  }
  return ret;
}
void
socket_client_event(GSocketClient      *client,
               GSocketClientEvent  event,
               GSocketConnectable *connectable,
               GIOStream          *connection,
               gpointer            user_data)
{
  static GEnumClass *event_class;

  if (!event_class)
    event_class = g_type_class_ref(G_TYPE_SOCKET_CLIENT_EVENT);
  g_debug("L%d f-%s event:%s", __LINE__, __func__,
      g_enum_get_value(event_class, event)->value_nick);
  if (!g_strcmp0(g_enum_get_value(event_class, event)->value_nick, "connecting")) {
  }
}
void *tcp_client_open(const char *ip, int port)
{
  GSocketClient* socket;
  GSocketConnection* conn;
  GError *error = NULL;
  GObject *self = NULL;

  socket = g_socket_client_new();
  g_signal_connect(socket, "event", G_CALLBACK(socket_client_event),
      NULL);
  g_socket_client_set_timeout(socket, 6*60);
  conn = g_socket_client_connect_to_host(socket, ip, port, NULL,
      &error);
  if (!conn) {
    g_warning("[%d]%s:%s port:%d", __LINE__, __func__,
        error->message, port);
    g_clear_error(&error);
    g_object_unref(socket);
    return NULL;
  }
  g_object_unref(socket);
  GIOChannel* s_ch =g_io_channel_unix_new(g_socket_get_fd(
        g_socket_connection_get_socket(conn)));
  if (!s_ch) {
    g_print("Failed to open the serial port\n");
    g_object_unref(conn);
    return NULL;
  }
  g_debug("L%d f-%s %s[%d]conn:%p", __LINE__, __func__,
      ip, port, conn);
  self = G_OBJECT(socket_socket_new());
  g_object_set(self, "socket", socket, "ip", ip, "port", port,
      "conn", conn, NULL);
  return self;
}
static void
read_async_cb_tcp_to_serial(GObject *source_object,
                        GAsyncResult *res,
                        gpointer user_data)
{
  GError *error = NULL;
  gsize bytes_read, bytes_write;
  GObject *self = user_data;
  gchar *buffer = NULL;
  gint bufferSize;
  //GInputStream *istream = NULL;
  GSocketConnection *conn = NULL;
  GSocket *socket = NULL;

  g_object_get(self, "conn", &conn, NULL);
  socket = g_socket_connection_get_socket(conn);
  if (g_socket_is_closed(socket)) {
    g_warning("L%d f-%s disconnected", __LINE__, __func__);
    return;
  }
  if (!g_socket_connection_is_connected(conn)) {
    g_warning("L%d f-%s disconnected", __LINE__, __func__);
    return;
  }

  g_debug("L%d f-%s", __LINE__, __func__);
  bytes_read = g_input_stream_read_finish(
      G_INPUT_STREAM(source_object), res, &error);
  if (error) {
    g_warning("L%d f-%s error:%s", __LINE__, __func__, error->message);
    GSocket *socket = g_socket_connection_get_socket (conn);
    g_socket_close(socket, NULL);
    g_clear_error(&error);
    return;
  }
  g_object_get(self, "buffer", &buffer, "bufferSize", &bufferSize,
      NULL);
  if (bytes_read) {
    //g_debug("read[%d]%p self:%p", bytes_read, buffer, self);
    print_hex((uint8_t *)buffer, bytes_read);
    uint8_t *new_data = NULL;
    GIOChannel *channel = NULL;
    func_send handle;
    size_t new_size = 0;
    g_object_get(self, "channel", &channel, "funcPrefixSend", &handle,
        NULL);
    if (handle) {
      handle(channel, (uint8_t *)buffer, bytes_read, self);
    }
    new_data = (uint8_t *)buffer;
    new_size = bytes_read;
    g_io_channel_write_chars(channel, (gchar *)new_data, new_size,
        &bytes_write, &error);
    if (error) {
      g_warning("L%d f-%s error:%s", __LINE__, __func__,
          error->message);
      g_clear_error(&error);
      return;
    } else if (bytes_read == bytes_write) {
      g_io_channel_flush(channel, &error);
      if (error) {
        g_warning("L%d f-%s error:%s", __LINE__, __func__,
            error->message);
        g_clear_error(&error);
        return;
      }
    } else {
      g_warning(
          "L%d f-%s bytes not all:%"G_GSIZE_FORMAT",%"G_GSIZE_FORMAT,
          __LINE__, __func__, bytes_read, bytes_write);
      exit(1);
    }
  } else {
    g_warning("L%d f-%s disconnected", __LINE__, __func__);
    g_socket_close(socket, NULL);
    return;
  }
  GInputStream *istream = NULL;
  istream = g_io_stream_get_input_stream(G_IO_STREAM(conn));
  g_input_stream_read_async(istream,
      buffer, bufferSize, G_PRIORITY_DEFAULT,
      NULL,(GAsyncReadyCallback) read_async_cb_tcp_to_serial,
      self);
  return;
}
void tcp_to_serial(void *obj, void *serial)
{
  GObject *self = obj;
  GInputStream *istream = NULL;
  GSocketConnection* conn;
  gchar *buffer;
  gint bufferSize;

  g_object_set(self, "channel", serial, NULL);
  g_object_get(self, "conn", &conn,
      "buffer", &buffer, "bufferSize", &bufferSize, NULL);
 // g_debug("buffer:%p self:%p", buffer, self);
//read
  istream = g_io_stream_get_input_stream(G_IO_STREAM(conn));
  g_input_stream_read_async(istream, buffer,
      bufferSize, G_PRIORITY_DEFAULT,
      NULL, (GAsyncReadyCallback)read_async_cb_tcp_to_serial, self);
}
/*
static void
read_read_write_async_cb (GInputStream *istream,
                          GAsyncResult *result,
                          gpointer      user_data)
{
  ReadAsyncData *data = user_data;
  GError *error = NULL;
  gboolean res;
  gsize bytes_read;
  GSocketConnection *conn;

  res = g_input_stream_read_all_finish (istream, result, &bytes_read, &error);
  g_assert_no_error (error);
  g_assert_true (res);

  if (data->handle)
    data->handle(data->data, &bytes_read, data->user_data);

  conn = data->conn;
  g_object_set_data (G_OBJECT (conn), "test-data-read", GINT_TO_POINTER (TRUE));

  g_input_stream_read_all_async (istream,
                                 data->data,
                                 20,
                                 G_PRIORITY_DEFAULT,
                                 NULL,
                                 (GAsyncReadyCallback) read_read_write_async_cb,
                                 data );
}

static void
incoming_read_write_async_cb (GSocketService    *service,
                              GSocketConnection *conn,
                              GObject           *source_object,
                              gpointer           user_data)
{
  ReadAsyncData *data;
  GSocketConnection **cconn = user_data;
  GInputStream *istream;

  istream = g_io_stream_get_input_stream (G_IO_STREAM (conn));

  data = g_new0 (ReadAsyncData, 1);
  data->conn = g_object_ref (conn);
  data->data = g_new0 (guint8, 20);
  data->handle = g_object_get_data(G_OBJECT(service), "handle");
  data->user_data = g_object_get_data(G_OBJECT(service), "user_data");

  g_input_stream_read_all_async (istream,
                                 data->data,
                                 20,
                                 G_PRIORITY_DEFAULT,
                                 NULL,
                                 (GAsyncReadyCallback) read_read_write_async_cb,
                                 data);

  *cconn = g_object_ref (conn);
}

void *tcp_server_open(int port)
{
  GError *error = NULL;
  GSocketService *service = NULL;
  GInetAddress *addr = NULL;
  GSocketAddress *saddr = NULL, *listening_addr;

  addr = g_inet_address_new_any(G_SOCKET_FAMILY_IPV4);
  saddr = g_inet_socket_address_new(addr, port);
  g_object_unref(addr);
  service = g_socket_service_new();
  g_socket_listener_add_address (G_SOCKET_LISTENER (service),
                               saddr,
                               G_SOCKET_TYPE_STREAM,
                               G_SOCKET_PROTOCOL_TCP,
                               NULL,
                               &listening_addr,
                               &error);
  g_assert_no_error (error);
  g_object_unref (saddr);

  g_signal_connect(service, "incoming", G_CALLBACK (incoming_read_write_async_cb),
      NULL);
  return service;
}
void *tcp_server_open1(int port, void *handle, void *user_data)
{
  gpointer service = NULL;

  service = tcp_server_open(port);
  g_object_set_data(service, "handle", handle);
  g_object_set_data(service, "user_data", user_data);

  return service;
}
*/
